﻿using Excel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
using ZyGame.Avatar;
using ZyGame.Editor.AssetBuilder;
using Object = UnityEngine.Object;

namespace ZyGame.Editor.BuilderProcess
{
    internal class ImportExcelConfigPorcess
    {
        class ConfigHeader
        {
            public string name;
            public string type;
            public string describe;
        }

        public static void Import()
        {
            if (Directory.Exists(AssetBuilderSetting.instance.ConfigOptions.importPath) is false)
            {
                return;
            }

            string[] files = Directory.GetFiles(AssetBuilderSetting.instance.ConfigOptions.importPath);
            List<string> failList = new List<string>();
            foreach (string file in files)
            {
                string s = ReadExcel(file);
                if (s.IsNullOrEmpty() is false)
                {
                    failList.Add(s);
                }
            }

            if (failList.Count > 0)
            {
                EditorUtility.DisplayDialog("提示", $"导入配置表完成,但是其中[{string.Join(',', failList)}]导入失败，详情请查看控制台。", "OK");
            }
            else
            {
                EditorUtility.DisplayDialog("提示", "导入配置表完成", "OK");
            }
        }

        public static string ReadExcel(string path)
        {
            string fileName = Path.GetFileNameWithoutExtension(path).Replace(" ", string.Empty);
            try
            {
                DataTable data = GetDataTable(path);
                if (data is null)
                {
                    return fileName;
                }

                List<ConfigHeader> headers = GetDataTableHeaders(data);
                if (headers.Count is 0)
                {
                    Debug.LogError($"没有找到表头，请确认[{fileName}]是否存在表头行");
                    return fileName;
                }

                // if (WriteScriptable(path, headers) is not null)
                // {
                //     return fileName;
                // }

                if (CreateScriptableObjectAssetFile(fileName, headers, data) is not null)
                {
                    return fileName;
                }

                return string.Empty;
            }
            catch (Exception e)
            {
                Debug.Log(fileName + " 导入失败." + e);
                return fileName;
            }
        }

        private static DataTable GetDataTable(string path)
        {
            try
            {
                FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
                IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
                DataSet dataSet = excelReader.AsDataSet();
                if (dataSet.Tables.Count is 0)
                {
                    Debug.LogError("不存在数据表格");
                    return default;
                }

                return dataSet.Tables[0];
            }
            catch (IOException e)
            {
                Debug.LogError(e);
                return default;
            }
        }

        private static List<ConfigHeader> GetDataTableHeaders(DataTable data)
        {
            List<ConfigHeader> headers = new List<ConfigHeader>();
            if (data.Rows.Count < 3)
            {
                return headers;
            }

            for (int i = 0; i < data.Rows[0].ItemArray.Length; i++)
            {
                string fieldName = data.Rows[0][i].ToString();
                string fieldType = data.Rows[1][i].ToString();
                string fieldDescribe = data.Rows[2][i].ToString();
                headers.Add(new ConfigHeader()
                {
                    name = fieldName,
                    type = fieldType,
                    describe = fieldDescribe,
                });
            }

            return headers;
        }

        private static Exception CreateScriptableObjectAssetFile(string fileName, List<ConfigHeader> headers, DataTable data)
        {
            try
            {
                string output = AssetDatabase.GetAssetPath(AssetBuilderSetting.instance.ConfigOptions.configDataOutput);
                string templatePath = AssetBuilderSetting.instance.ConfigOptions.nameSpace + "." + fileName;
                Type configType = AppDomain.CurrentDomain.GetTypeWithFullName(templatePath);
                Type templateType = AppDomain.CurrentDomain.GetTypeWithFullName(templatePath + "Item");

                // ScriptableObject target = ScriptableObject.CreateInstance(configType);
                // string assetPath = $"{output}/{fileName}.asset";
                // if (File.Exists(assetPath))
                // {
                //     AssetDatabase.DeleteAsset(assetPath);
                // }
                //
                // AssetDatabase.CreateAsset(target, assetPath);
                // AssetDatabase.SaveAssets();
                // AssetDatabase.Refresh();

                IList dataList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(templateType));
                for (int i = 3; i < data.Rows.Count; i++) //遍历行
                {
                    if (data.Rows[i].IsNull(0))
                    {
                        continue;
                    }

                    object item = Activator.CreateInstance(templateType);
                    bool state = true;
                    for (int j = 0; j < headers.Count; j++)
                    {
                        if (data.Rows[i][j] == DBNull.Value)
                        {
                            continue;
                        }

                        FieldInfo field = templateType.GetField(headers[j].name);
                        if (field is null)
                        {
                            Debug.Log($"获取字段[{headers[j].name}]失败");
                            continue;
                        }

                        try
                        {
                            field.SetValue(item, Convert.ChangeType(data.Rows[i][j], headers[j].type.GetTypeWithStringName()));
                        }
                        catch (Exception e)
                        {
                            state = false;
                            Debug.LogError($"设置[{templatePath}Item.{headers[j].name}]字段失败.\n{e.Message}");
                        }
                    }

                    if (state)
                    {
                        dataList.Add(item);
                    }
                }

                // FieldInfo dataListField = configType.GetField("dataList");
                // dataListField.SetValue(target, dataList);
                // EditorUtility.SetDirty(target);
                File.WriteAllText($"{output}/{fileName}.json", Newtonsoft.Json.JsonConvert.SerializeObject(dataList));

                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();


                return default;
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                return e;
            }
        }

        private static Exception WriteScriptable(string path, List<ConfigHeader> headers)
        {
            try
            {
                string output = AssetDatabase.GetAssetPath(AssetBuilderSetting.instance.ConfigOptions.configDataOutput);
                string fileName = Path.GetFileNameWithoutExtension(path).Replace(" ", string.Empty);
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("using System;");
                sb.AppendLine("using ZyGame;");
                sb.AppendLine("using UnityEngine;");
                sb.AppendLine("using System.Collections.Generic;");
                sb.AppendLine("namespace " + AssetBuilderSetting.instance.ConfigOptions.nameSpace);
                sb.AppendLine("{");
                sb.AppendLine($"\t[PathOptions(\"{output}/{fileName}.asset\", PathOptions.Localtion.Packaged)]");
                sb.AppendLine($"\tpublic class {fileName} : Config<{fileName}>");
                sb.AppendLine("\t{");
                sb.AppendLine("\t\t[SerializeField]");
                sb.AppendLine($"\t\tpublic List<{fileName}Item> dataList;");
                sb.AppendLine($"\t\tpublic {fileName}Item Get({headers[0].type} value)");
                sb.AppendLine("\t\t{");
                sb.AppendLine($"\t\t\treturn dataList.Find(x => x.{headers[0].name} == value);");
                sb.AppendLine("\t\t}");


                sb.AppendLine("\t}");
                sb.AppendLine("");
                sb.AppendLine("\t[Serializable]");
                sb.AppendLine($"\tpublic class {fileName}Item");
                sb.AppendLine("\t{");
                for (int i = 0; i < headers.Count; i++)
                {
                    sb.AppendLine("\t\t/// <summary>");
                    sb.AppendLine($"\t\t/// {headers[i].describe}");
                    sb.AppendLine("\t\t/// </summary>");
                    sb.AppendLine($"\t\tpublic {headers[i].type} {headers[i].name};");
                }

                sb.AppendLine("\t}");
                sb.AppendLine("}");

                string outScriptPath = AssetDatabase.GetAssetPath(AssetBuilderSetting.instance.ConfigOptions.scriptOutput);
                string csFile = outScriptPath + "/" + fileName + ".cs";
                if (File.Exists(csFile))
                {
                    File.Delete(csFile);
                }

                File.WriteAllText(csFile, sb.ToString());
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
                return default;
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                return e;
            }
        }
    }
}